﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JScriptSuite.QA.Lib.System
{
    class VarianceTests
    {
        internal static JResultItem AllTests()
        {
            return JResultItem.CreateInstance("VarianceTests")
                .Push(CompilerTests(), IsOperatorTests(), InstanceOfTests(), EnumeratorTest());
        }

        static JResultItem CompilerTests()
        {
            I<Base, Dev1> ib1 = new C<Base, Dev1>();

            return JResultItem.CreateInstance("CompilerTests")
                .Push(ib1 is I<Base, Dev1>,
                    ib1 is I<Dev2, Base>,
                    new C<Base, Dev1>() is I<Dev2, Base>,
                    new C<Base, Dev1>() is I<Base, Dev1>,
                    new C<Base, Dev1>() is I<Dev1, Base>,
                    new C<Base, Dev1>() is I<Base, Base>,
                    new C<Dev2, Dev1>() is I<Base, Dev1>
                );
        }

        static JResultItem IsOperatorTests()
        {
            I<Base, Dev1> ib1 = new C<Base, Dev1>();
            return JResultItem.CreateInstance("IsOperatorTests")
                .Push((object)ib1 is I<Base, Dev1>,
                    (object)ib1 is I<Base, Base>,
                    (object)ib1 is I<Dev2, Base>,
                    (object)new C<Base, Dev1>() is I<Dev2, Base>,
                    (object)new C<Base, Dev1>() is I<Base, Dev1>,
                    (object)new C<Base, Dev1>() is I<Dev1, Base>,
                    (object)new C<Base, Dev1>() is I<Base, Base>,
                    (object)new C<Dev2, Dev1>() is I<Base, Dev1>,
                    (object)new C<Base, int>() is I<Base, object>,
                    (object)new C<Base, string[]>() is I<Base, object[]>,
                    (object)new C<Base, int[][]>() is I<Base, object[]>,
                    (object)new C<Base, string[][]>() is I<Base, string[]>
                );
        }

        static JResultItem InstanceOfTests()
        {
            I<Base, Dev1> ib1 = new C<Base, Dev1>();
            return JResultItem.CreateInstance("InstanceOfTests")
                .Push(typeof(I<Base, Dev1>).IsInstanceOfType(ib1),
                    typeof(I<Base, Base>).IsInstanceOfType(ib1),
                    typeof(I<Dev2, Base>).IsInstanceOfType(ib1),
                    typeof(I<Dev2, Base>).IsInstanceOfType(new C<Base, Dev1>()),
                    typeof(I<Base, Dev1>).IsInstanceOfType(new C<Base, Dev1>()),
                    typeof(I<Dev1, Base>).IsInstanceOfType(new C<Base, Dev1>()),
                    typeof(I<Base, Base>).IsInstanceOfType(new C<Base, Dev1>()),
                    typeof(I<Base, Dev1>).IsInstanceOfType(new C<Dev2, Dev1>())
                );
        }

        static JResultItem EnumeratorTest()
        {
            Dev1[] ar = new Dev1[10];
            IEnumerable<IBase> enumerable = ar;
            int count = 0;
            foreach (IBase b in enumerable)
            {
                count++;
            }

            return JResultItem.CreateInstance("EnumeratorTest").Push(count);

        }

        interface IBase
        {

        }
        
        class Base : IBase
        {
        }

        class Dev1 : Base
        {

        }

        class Dev2 : Base
        {

        }

        class C<T1, T2> : I<T1, T2> where T1 : Base
        {
            T1 I<T1, T2>.Data1
            {
                set { throw new NotImplementedException(); }
            }

            T2 I<T1, T2>.Data2
            {
                get { throw new NotImplementedException(); }
            }
        }

        interface I<in T1, out T2> where T1 : Base
        {
            T1 Data1 { set; }   // contravariant
            T2 Data2 { get; }   // covariant
        }
    }
}
